﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using Styx;
using Styx.Common;
using Styx.Common.Helpers;
using Styx.CommonBot;
using Styx.CommonBot.AreaManagement;
using Styx.CommonBot.Database;
using Styx.CommonBot.Frames;
using Styx.CommonBot.Inventory;
using Styx.CommonBot.Profiles;
using Styx.CommonBot.POI;
using Styx.CommonBot.Routines;
using Styx.Helpers;
using Styx.Pathing;
using Styx.Plugins;
using Styx.WoWInternals;
using Styx.WoWInternals.Misc;
using Styx.WoWInternals.World;
using Styx.WoWInternals.WoWObjects;
using Styx.TreeSharp;

using Sequence = Styx.TreeSharp.Sequence;
using Action = Styx.TreeSharp.Action;
using CommonBehaviors.Actions;
using Levelbot.Actions.Combat;

namespace Styx.Bot.CustomBots
{
    public class MBoxFollower : BotBase
    {
        // ********************** Pet Target Enable/Disable *************************
        private bool pettarget = true; // Use leaders pet's target when leader has no target
        private bool lootinside = true; // Loot when inside buildings
        private bool lootininstance = false; // Loot when in an instance
        private bool lootatall = true; // Loot at all
        private bool Skinning = false; // Enable skinning?
        private bool NinjaSkin = false; // Skin other peoples kills
        private bool AutoQuestItem = true; // Look for quest items on the ground, such as flowers and whatnot
        private int DistanceForQuestItem = 20; // How far to search for quest items
        private bool ManualLeader = false; // If true, target the person you want to be leader, then hit start
        private int distance_to_follow = 5; // How close to follow the leader, this is in feet
        private bool AutoTargetQG = true; //Auto Target RaF Leader's target is they have a quest you can pick up or turn in. (Will not turn in or pick up quest automatically)
        // ********************** Do not edit anything below this line **************

        public override string Name { get { return string.Format("Multibox Follower"); }}

        private Composite _root;

        public override Composite Root
        {
            get
            {
                return _root ?? (_root =
                    new PrioritySelector(
                        CreateCombatBehavior()
                        )
                    );
            }
        }


        public override PulseFlags PulseFlags
        {
            get { return PulseFlags.All; }
        }

        public override void Start()
        {
            Targeting.Instance.IncludeTargetsFilter += IncludeTargetsFilter;
            GlobalSettings.Instance.LogoutForInactivity = false;
        }

        public override void Stop()
        {
            Targeting.Instance.IncludeTargetsFilter -= IncludeTargetsFilter;
            GlobalSettings.Instance.LogoutForInactivity = true;
        }

        private static void IncludeTargetsFilter(List<WoWObject> incomingUnits, HashSet<WoWObject> outgoingUnits)
        {
            if (StyxWoW.Me.GotTarget && StyxWoW.Me.CurrentTarget.Attackable)
            {
                outgoingUnits.Add(StyxWoW.Me.CurrentTarget);
            }
        }

        private bool FirstPulse = false;
        private bool islooting = false;
        private bool isMounting = false;
        private string mycorpse = null;
        public static LocalPlayer Me { get { return StyxWoW.Me; } }
        public static bool IsInGroup { get { return Me.GroupInfo.IsInRaid || Me.GroupInfo.IsInParty; } }

        public static WoWPoint _lastDest;
        private List<WoWUnit> LootBlacklist;
        private List<WoWUnit> LootList;
        private List<WoWUnit> SkinList;
        private List<WoWGameObject> QuestItems;
        private bool ManLeadFound = false;
        private bool QuestGiverTargeted = false;
        private WoWPlayer Leader;

        private void slog(string format, params object[] args)
        {
            Logging.Write("[Follower] " + format, args);
        }

        private static Stopwatch lootTimer = new Stopwatch();


        //Loot routine
        private void Loot()
        {
            if (AutoQuestItem && LootList.Count == 0)
            {
                QuestItems = ObjectManager.GetObjectsOfType<WoWGameObject>()
                .Where(o => (o.SubType == WoWGameObjectType.Chest || o.SubType == WoWGameObjectType.Goober) && o.CanLoot && o.Distance <= DistanceForQuestItem && !o.IsChest && !o.IsHerb && !o.IsMineral)
                .OrderBy(o => o.Distance).ToList();

                foreach (WoWGameObject QuestItem in QuestItems)
                {
                    if (QuestItems.Count >= 1)
                    {
                        if (QuestItems[0].WithinInteractRange)
                        {
                            lootTimer.Start();
                            WoWMovement.MoveStop();
                            QuestItems[0].Interact();
                            slog("Looting Quest Item " + QuestItems[0].Name);
                            LootFrame.Instance.LootAll();
                            Thread.Sleep(500);
                            islooting = true;
                        }
                        else
                        {
                            lootTimer.Start();
                            islooting = true;
                            slog("Found Quest Item " + QuestItems[0].Name + ", going to loot it");
                            Navigator.MoveTo(QuestItems[0].WorldLocation);
                        }
                    }
                }
                islooting = false;
            }

            if (lootatall)
            {
                if (Skinning)
                {
                    foreach (WoWUnit Skinable in SkinList)
                    {
                        if (Me.CurrentTarget == null)
                        {
                            Skinable.Target();
                            Thread.Sleep(500);
                        }
                        else if ((Me.CanSkinLevel >= Me.CurrentTarget.Level && Me.CurrentTarget.CanSkin || Me.CurrentTarget.CanLoot) && Me.CurrentTarget.IsDead)
                        {
                            if (Me.CurrentTarget.WithinInteractRange)
                            {
                                lootTimer.Start();
                                WoWMovement.MoveStop();
                                WoWMovement.Face();
                                Me.CurrentTarget.Interact();
                                LootFrame.Instance.LootAll();
                                Thread.Sleep(500);
                            }
                            else
                            {
                                Navigator.MoveTo(Me.CurrentTarget.WorldLocation);
                            }
                        }
                        else if (!Me.CurrentTarget.CanLoot)
                        {
                            Me.ClearTarget();
                            lootTimer.Stop();
                        }
                    }
                }

                foreach (WoWUnit Lootable in LootList)
                {
                    if (Me.CurrentTarget == null)
                    {
                        Lootable.Target();
                        Thread.Sleep(500);
                    }
                    else if (Me.CurrentTarget.CanLoot && Me.CurrentTarget.IsDead)
                    {
                        if (Me.CurrentTarget.WithinInteractRange)
                        {
                            lootTimer.Start();
                            islooting = true;
                            WoWMovement.MoveStop();
                            WoWMovement.Face();
                            slog("Looting" + Me.CurrentTarget);
                            Me.CurrentTarget.Interact();
                            LootFrame.Instance.LootAll();
                            Thread.Sleep(500);
                        }
                        else
                        {
                            Navigator.MoveTo(Me.CurrentTarget.WorldLocation);
                            islooting = true;
                        }
                    }
                    else if (!Me.CurrentTarget.CanLoot)
                    {
                        //Me.ClearTarget();
                        lootTimer.Stop();
                        islooting = false;
                    }
                }
            }
        }

        private void populate()
        {
            if (AutoQuestItem)
            {
                if (Skinning)
                {
                    if (NinjaSkin)
                    {
                    SkinList = ObjectManager.GetObjectsOfType<WoWUnit>()
                    .Where(u => !u.IsAlive && u.CanSkin && u.Level <= Me.CanSkinLevel && u.Distance <= 35)
                    .OrderBy(u => u.Distance).ToList();
                    }
                    else
                    {
                    SkinList = ObjectManager.GetObjectsOfType<WoWUnit>()
                    .Where(u => !u.IsAlive && u.CanSkin && u.Level <= Me.CanSkinLevel && u.KilledByMe && u.Distance <= 35)
                    .OrderBy(u => u.Distance).ToList();
                    }
                }
                LootList = ObjectManager.GetObjectsOfType<WoWUnit>()
                .Where(u => !u.IsAlive && u.CanLoot && u.Distance <= 35)
                .OrderBy(u => u.Distance).ToList();
            }
        }

        private void timer()
        {
            if (lootTimer.ElapsedMilliseconds > 6000)
            {
                lootTimer.Reset();
                lootTimer.Start();
            }
        }
        private static int randnum(int min, int max)
        {
            Random random = new Random();
            return random.Next(min, max);
        }

        private void selleader()
        {
            if (FirstPulse)
            {
                if (!ManLeadFound && ManualLeader)
                {
                    slog("****************************************");
                    slog("Manual Leader selection enabled, please target the player you wish to be leader, you have 10 seconds");
                    slog("****************************************");
                    Thread.Sleep(10000);

                    if (Me.CurrentTarget.IsPlayer)
                    {
                        slog("Current Target is " + Me.CurrentTarget.Name + ", attempting to set as leader");
                        Leader = Me.CurrentTarget.ToPlayer();
                    }
                }
                else if (!ManualLeader)

                try
                {
                    Leader = RaFHelper.Leader ?? Me.PartyMembers.FirstOrDefault();
                }

                catch { }

                try
                {
                    {
                        if (!ManLeadFound)
                        {
                            slog("Setting Leader to " + Leader.Name);
                            ManLeadFound = true;
                        }
                        slog("Setting Leader to " + Leader.Name);
                        RaFHelper.SetLeader(Leader.Guid);
                        FirstPulse = false;
                    }
                }
                catch { }
            }
        }

        private void movement()
        {
            try
            {
                    //{
                if (!isMounting && RaFHelper.Leader.Mounted && !Me.Mounted && !Me.IsCasting && (!Me.IsInInstance || !Me.IsIndoors))
                {
                    isMounting = true;
                    WoWMovement.MoveStop();
                    slog(RaFHelper.Leader.Name + " has mounted, mounting too. ");
                    Lua.DoString("RunMacro('Mount')");
                    StyxWoW.SleepForLagDuration();
                    //WoWPoint movePoint = WoWMathHelper.CalculatePointFrom(Me.Location, RaFHelper.Leader.WorldLocation, distance_to_follow);
                    //Navigator.MoveTo(movePoint);
                    WoWMovement.MoveStop();
                    Thread.Sleep(500);
                    isMounting = false;
                }

                if (!RaFHelper.Leader.Mounted && Me.Mounted)
                {
                    if (Me.IsFlying)
                    {
                        slog("Leader has dismounted, but im still flying, coming in for a landing");
                        if (RaFHelper.Leader.Distance < 10)
                        {
                            Mount.Dismount();
                        }

                    }
                    else
                    {
                        slog(RaFHelper.Leader.Name + " has dismounted, dismounting");
                        if (Me.Mounted && RaFHelper.Leader.Distance < 10)
                        {
                            Mount.Dismount();
                        }
                        WoWPoint movePoint = WoWMathHelper.CalculatePointFrom(Me.Location, RaFHelper.Leader.WorldLocation, distance_to_follow);
                        Navigator.MoveTo(movePoint);
                    }
                }

                if (!isMounting && !islooting && !QuestGiverTargeted && RaFHelper.Leader.IsAlive && Me.IsAlive && !RaFHelper.Leader.Combat && RaFHelper.Leader.Distance > distance_to_follow )
                    //(!AutoQuestItem || QuestItems.Count == 0) && LootList.Count == 0 && (!Skinning || SkinList.Count == 0) || Me.NormalBagsFull ) &&
                {
                    WoWPoint pt = RaFHelper.Leader.Location;
                    if (pt.DistanceSqr(_lastDest) > 1 || !Me.IsMoving && !Me.IsCasting)
                    {
                        _lastDest = pt;
                        WoWPoint newPoint = WoWMovement.CalculatePointFrom(pt, (float) 0.85 * distance_to_follow);
                        slog("Moving to Leader @ {0:F1} yds", pt.Distance(Me.Location));
                        if ( RaFHelper.Leader != null && RaFHelper.Leader.Mounted && (RaFHelper.Leader.IsFlying || RaFHelper.Leader.IsSwimming) )
                            Flightor.MoveTo(newPoint);
                        else
                            Navigator.MoveTo(newPoint);
                    }
                }

                if (lootatall)
                {
                    if (!RaFHelper.Leader.Combat && RaFHelper.Leader.IsAlive && !Me.Combat && !Me.NormalBagsFull )
                    {
                        if (!Me.IsInInstance && !Me.IsIndoors || Me.IsInInstance && lootininstance || Me.IsIndoors && lootinside)
                        {
                            Loot();
                        }
                    }
                }

                    //}
                    // catch
                    /*
                    {
                        slog("Appears we are having an issue with the leader, attempting to relocate");


                        WoWPoint movePoint = WoWMathHelper.CalculatePointFrom(Me.Location, RaFHelper.Leader.WorldLocation, distance_to_follow);
                        Navigator.MoveTo(movePoint);
                        //FirstPulse = true;
                        //    Thread.Sleep(5000);
                        //    selleader();
                    }
                    */
                
            }
            catch
            {
                FirstPulse = true;
        	selleader();
		slog("RaFhelper.Leader.Name=" + RaFHelper.Leader.Name); 
            }
        }

        private void combat()
        {
            try
            {
                if (RaFHelper.Leader.IsValid)
                {
                    if (RaFHelper.Leader.Combat || RaFHelper.Leader.Combat && RaFHelper.Leader.GotAlivePet && pettarget)

                    //if (RaFHelper.Leader.CurrentTarget != null && Me.CurrentTarget != RaFHelper.Leader.CurrentTarget || Me.CurrentTarget != RaFHelper.Leader.Pet.CurrentTarget)
                    {
                        //if (RaFHelper.Leader.CurrentTarget != null)
                        //{
                        if (RaFHelper.Leader.CurrentTarget != null)
                        {
                            RaFHelper.Leader.CurrentTarget.Target();
                        }
                        if (pettarget && RaFHelper.Leader.GotAlivePet)
                        {
                            if (RaFHelper.Leader.CurrentTarget == null && RaFHelper.Leader.Pet.CurrentTarget != null)
                            {
                                RaFHelper.Leader.Pet.CurrentTarget.Target();
                            }
                        }
                        if (!Me.Combat)
                        {
                            if (RaFHelper.Leader.CurrentTarget != null && (!Me.IsCasting || !Me.IsAutoAttacking))
                            {
                                TreeRoot.StatusText = RaFHelper.Leader.Name + " is attacking " + RaFHelper.Leader.CurrentTarget.Name + ", helping out!";
                            }
                            else if (pettarget && !Me.IsCasting && RaFHelper.Leader.GotAlivePet && RaFHelper.Leader.CurrentTarget == null && RaFHelper.Leader.Pet.CurrentTarget != null)
                            {
                                TreeRoot.StatusText = RaFHelper.Leader.Name + "' pet " + RaFHelper.Leader.Pet.Name + " is attacking " + RaFHelper.Leader.Pet.CurrentTarget.Name + ", helping out!";
                            }
                        }
                    }
                    //}

                    //}
                }
            }
            catch
            {
            }
        }

        private void TargetRafQGT()
        {

            try
            {
                if (AutoTargetQG && RaFHelper.Leader.CurrentTarget != null)
                {
                    if (RaFHelper.Leader.CurrentTarget.QuestGiverStatus == QuestGiverStatus.TurnIn || RaFHelper.Leader.CurrentTarget.QuestGiverStatus == QuestGiverStatus.Available || RaFHelper.Leader.CurrentTarget.QuestGiverStatus == QuestGiverStatus.AvailableRepeatable || RaFHelper.Leader.CurrentTarget.QuestGiverStatus == QuestGiverStatus.TurnInRepeatable || RaFHelper.Leader.CurrentTarget.InteractType == WoWInteractType.TaxiPathAvailable)
                    {
                        QuestGiverTargeted = true;
                        if (Me.CurrentTarget != RaFHelper.Leader.CurrentTarget)
                        {
                            RaFHelper.Leader.CurrentTarget.Target();
                        }
                        else
                        {
                            if (Me.CurrentTarget.WithinInteractRange)
                            {
                                WoWMovement.MoveStop();
                                Me.CurrentTarget.Interact();
                            }
                            else
                            {
                                Navigator.MoveTo(Me.CurrentTarget.WorldLocation);
                            }

                        }

                    }
                    else
                    {
                        if (QuestGiverTargeted)
                        {
                            Me.ClearTarget();
                            QuestGiverTargeted = false;
                        }
                    }
                }
            }
            catch
            {
            }
        }

        private void IsDead()
        {
            if (Me.IsDead)
            {
                Lua.DoString("RepopMe()");
            }

            if (Me.IsGhost)
            {
                if (Battlegrounds.IsInsideBattleground)
                {
                    slog("Waiting for spirit rez");
                }
                else
                {
                    Navigator.MoveTo(Me.CorpsePoint);
                    try
                    {
                        {
                            if (!RaFHelper.Leader.IsDead && !RaFHelper.Leader.IsGhost)
                            {
                                Lua.DoString("RetrieveCorpse()");
                            }
                        }
                    }
                    catch
                    {
                        FirstPulse = true;
                        //    slog("Error in Death Code");
                        selleader();

                    }
                }
            }

        }

        public override void Pulse()
        {
            populate();

            if (!Me.Combat && !FirstPulse)
            {
                //slog("Debug: Pulse first");
                TargetRafQGT();
                movement();
            }

            if (RoutineManager.Current.NeedRest && !Me.IsDead && !Me.IsGhost)
            {
                slog("Routine says we need rest. Letting it do it.");
                RoutineManager.Current.Rest();
            }
            else
            {
                if (!IsInGroup)
                {
                    slog("Not in party, please rectify this situation");
                }
                else
                {
                    selleader();
                    if (Me.IsGhost || Me.IsDead)
                    {
                        IsDead();
                    }
                    else
                    {
                        movement();
                        combat();
                    }
                }
            }
        }

        private static bool NeedPull(object context)
        {
            var target = StyxWoW.Me.CurrentTarget;

            if (target == null)
                return false;

            if (!target.InLineOfSight)
                return false;

            if (target.Distance > Targeting.PullDistance)
                return false;

            return true;
        }

        private static Composite CreateCombatBehavior()
        {
            return new PrioritySelector(

                new Decorator(ret => !StyxWoW.Me.Combat && !StyxWoW.Me.IsDead && !StyxWoW.Me.IsGhost,
                    new PrioritySelector(
                        new PrioritySelector(
                            new Decorator(ctx => RoutineManager.Current.PreCombatBuffBehavior != null, RoutineManager.Current.PreCombatBuffBehavior),
                            new Decorator(ctx => RoutineManager.Current.NeedPreCombatBuffs,
                                new Sequence(
                                    new Action(ret => TreeRoot.StatusText = "Applying pre-combat buffs"),
                                    new Action(ret => RoutineManager.Current.PreCombatBuff())
                                )
                            )
                        ),
                        new ActionDebugString("[Combat] Pull"),
                        new Decorator(ret => BotPoi.Current.Type == PoiType.Kill,
                            new PrioritySelector(
                                new Decorator(ret => Targeting.Instance.TargetList.Count != 0,
                                    new Decorator(ret => BotPoi.Current.AsObject != Targeting.Instance.FirstUnit && BotPoi.Current.Type == PoiType.Kill,
                                        new Sequence(
                                            new Action(ret => BotPoi.Current = new BotPoi(Targeting.Instance.FirstUnit, PoiType.Kill)),
                                            new Action(ret => BotPoi.Current.AsObject.ToUnit().Target())
                                        )
                                    )
                                ),
                                new Decorator(NeedPull,
                                    new PrioritySelector(
                                        new Decorator(ctx => RoutineManager.Current.PullBuffBehavior != null, RoutineManager.Current.PullBuffBehavior),
                                        new Decorator(ctx => RoutineManager.Current.PullBehavior != null, RoutineManager.Current.PullBehavior),
                                        new ActionPull()
                                    )
                                )
                            )
                        )
                    )
                ),

                new Decorator(ret => StyxWoW.Me.Combat,
                    new PrioritySelector(
                        new PrioritySelector(
                            new Decorator(ctx => RoutineManager.Current.HealBehavior != null,
                                new Sequence(RoutineManager.Current.HealBehavior,
                                    new Action(delegate { return RunStatus.Success; })
                                )
                            ),
                            new Decorator(ctx => RoutineManager.Current.NeedHeal,
                                new Sequence(
                                    new Action(ret => TreeRoot.StatusText = "Healing"),
                                    new Action(ret => RoutineManager.Current.Heal())
                                )
                            )
                        ),
                        new PrioritySelector(
                            new Decorator(ctx => RoutineManager.Current.CombatBuffBehavior != null,
                                new Sequence(RoutineManager.Current.CombatBuffBehavior,
                                    new Action(delegate { return RunStatus.Success; })
                                )
                            ),
                            new Decorator(ctx => RoutineManager.Current.NeedCombatBuffs,
                                new Sequence(
                                    new Action(ret => TreeRoot.StatusText = "Applying Combat Buffs"),
                                    new Action(ret => RoutineManager.Current.CombatBuff())
                                )
                            )
                        ),
                        new PrioritySelector(
                            new Decorator(ctx => RoutineManager.Current.CombatBehavior != null,
                                new PrioritySelector(RoutineManager.Current.CombatBehavior,
                                    new Action(delegate { return RunStatus.Success; })
                                )
                            ),
                            new Sequence(
                                new Action(ret => TreeRoot.StatusText = "Combat"),
                                new Action(ret => RoutineManager.Current.Combat())
                            )
                        )
                    )
                ),

                new Decorator(delegate(object context) { try { return RaFHelper.Leader.IsValid; } catch { return false; } },
                    new Decorator(ret => !StyxWoW.Me.IsDead && !StyxWoW.Me.IsGhost && RaFHelper.Leader.Combat,
                        new PrioritySelector(
                            new PrioritySelector(
                                new Decorator(ctx => RoutineManager.Current.HealBehavior != null,
                                    new Sequence(RoutineManager.Current.HealBehavior,
                                        new Action(delegate { return RunStatus.Success; })
                                    )
                                ),
                                new Decorator(ctx => RoutineManager.Current.NeedHeal,
                                    new Sequence(
                                        new Action(ret => TreeRoot.StatusText = "Healing"),
                                        new Action(ret => RoutineManager.Current.Heal())
                                    )
                                )
                            ),
                            new PrioritySelector(
                                new Decorator(ctx => RoutineManager.Current.CombatBuffBehavior != null,
                                    new Sequence(RoutineManager.Current.CombatBuffBehavior,
                                        new Action(delegate { return RunStatus.Success; })
                                    )
                                ),
                                new Decorator(ctx => RoutineManager.Current.NeedCombatBuffs,
                                    new Sequence(
                                        new Action(ret => TreeRoot.StatusText = "Applying Combat Buffs"),
                                        new Action(ret => RoutineManager.Current.CombatBuff())
                                    )
                                )
                            ),
                            new PrioritySelector(
                                new Decorator(ctx => RoutineManager.Current.CombatBehavior != null,
                                    new PrioritySelector(RoutineManager.Current.CombatBehavior,
                                        new Action(delegate { return RunStatus.Success; })
                                    )
                                ),
                                new Sequence(
                                    new Action(ret => TreeRoot.StatusText = "Combat"),
                                    new Action(ret => RoutineManager.Current.Combat())
                                )
                            )
                        )
                    )
                )
            );
        }
    }
}
